# -*- coding: utf-8 -*-

# graphic-pppoe-client - Easy graphic networking manager
#
# Author: GoTLiuM InSPiRiT <gotlium@gmail.com>
#
# Copyright 2010, GoTLiuM InSPiRiT <gotlium@gmail.com>.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.
import re
import dbus

MM_DBUS_PATH='/org/freedesktop/ModemManager'
MM_DBUS_MODEM_PATH='/org/freedesktop/ModemManager/Modems/'

MM_DBUS_SERVICE='org.freedesktop.ModemManager'
MM_DBUS_INTERFACE_MODEM='org.freedesktop.ModemManager.Modem'
DBUS_INTERFACE_PROPERTIES='org.freedesktop.DBus.Properties'
MM_DBUS_INTERFACE_GSM_CARD='org.freedesktop.ModemManager.Modem.Gsm.Card'
MM_DBUS_INTERFACE_USSD='org.freedesktop.ModemManager.Modem.Gsm.Ussd'
MM_DBUS_INTERFACE_SMS='org.freedesktop.ModemManager.Modem.Gsm.SMS'
MM_DBUS_INTERFACE_CONTACTS='org.freedesktop.ModemManager.Modem.Gsm.Contacts'
MM_DBUS_INTERFACE_MODEM_CDMA='org.freedesktop.ModemManager.Modem.Cdma'
MM_DBUS_INTERFACE_MODEM_GSM_NETWORK='org.freedesktop.ModemManager.Modem.Gsm.Network'

mm_reg = {
	0: "idle",
	1: "home",
	2: "searching",
	3: "denied",
	4: "unknown",
	5: "roaming"
}

mm_allowed = {
	0: "any",
	1: "2G preferred",
	2: "3G preferred",
	3: "2G only",
	4: "3G only"
}

mm_act = {
	0: "unknown",
	1: "GSM",
	2: "GSM Compact",
	3: "GPRS",
	4: "EDGE",
	5: "UMTS",
	6: "HSDPA",
	7: "HSUPA",
	8: "HSPA"
}

class WWAN(object):

	def _getModemPath(self, path):
		if str(path)[:1] != '/':
			return MM_DBUS_MODEM_PATH + str(path)
		return path

	def enableModem(self, path, flag=True):
		bus = dbus.SystemBus()
		proxy = bus.get_object(MM_DBUS_SERVICE, self._getModemPath(path))
		modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM)
		modem.Enable(flag)

	def disableModem(self, path):
		self.enableModem(path, False)

	def getModemsList(self):
		bus = dbus.SystemBus()
		manager_proxy = bus.get_object(MM_DBUS_SERVICE, MM_DBUS_PATH)
		manager_iface = dbus.Interface(manager_proxy, dbus_interface=MM_DBUS_SERVICE)
		modems = manager_iface.EnumerateDevices()

		if not modems:
			return "No modems found"

		for m in modems:
			proxy = bus.get_object(MM_DBUS_SERVICE, m)
			props_iface = dbus.Interface(proxy, dbus_interface=DBUS_INTERFACE_PROPERTIES)

			driver = props_iface.Get(MM_DBUS_INTERFACE_MODEM, 'Driver')
			mtype = props_iface.Get(MM_DBUS_INTERFACE_MODEM, 'Type')
			device = props_iface.Get(MM_DBUS_INTERFACE_MODEM, 'MasterDevice')

			strtype = ""
			if mtype == 1:
				strtype = "GSM"
			elif mtype == 2:
				strtype = "CDMA"

			return (str(m), strtype, str(driver), str(device))

	def sendSms(self, path, number, message):
		bus = dbus.SystemBus()

		proxy = bus.get_object(MM_DBUS_SERVICE, self._getModemPath(path))
		modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM)
		modem.Enable(True)

		msg_dict = dbus.Dictionary({
			dbus.String('number') : dbus.String(number),
			dbus.String('text') : dbus.String(message)
		}, signature=dbus.Signature("sv"))
		
		iface = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_SMS)
		try:
			modem.Enable(False)
			iface.Send(msg_dict)
			return True
		except:
			modem.Enable(False)
			return "Sending message failed"

	def getSms(self, path, id = None):
		bus = dbus.SystemBus()

		proxy = bus.get_object(MM_DBUS_SERVICE, self._getModemPath(path))
		modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM)
		modem.Enable(True)
		
		sms = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_SMS)
		if id:
			return sms.Get(id)
		else:
			return sms.List()

	def getContacts(self, path, id = None):
		bus = dbus.SystemBus()

		proxy = bus.get_object(MM_DBUS_SERVICE, self._getModemPath(path))
		modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM)
		modem.Enable(True)

		contacts = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_CONTACTS)
		try:
			return contacts.List()
		except:
			return 'Not supported.'

	def unlockSim(self, path, pinCode):
		if not len(pinCode) in range(4,9):
			return "PIN must be between 4 or 8 characters inclusive"
		if not pinCode.isdigit():
			return "PIN must be numeric"

		bus = dbus.SystemBus()
		proxy = bus.get_object(MM_DBUS_SERVICE, self._getModemPath(path))

		props = dbus.Interface(proxy, dbus_interface=DBUS_INTERFACE_PROPERTIES)
		req = props.Get(MM_DBUS_INTERFACE_MODEM, "UnlockRequired")
		if req == "":
			return "SIM unlocked"

		if req != "sim-pin":
			return "Only sim-pin unlock supported for now"

		card = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_GSM_CARD)
		try:
			card.SendPin(pinCode)
		except Exception, e:
			return "Unlock failed: %s" % e

		req = props.Get(MM_DBUS_INTERFACE_MODEM, "UnlockRequired")
		if req != "":
			return "Unlock not successful: %s" % req

		return True

	def ussdSend(self, path, ussd):
		self.enableModem(path)
		
		bus = dbus.SystemBus()
		proxy = bus.get_object(MM_DBUS_SERVICE, self._getModemPath(path))

		modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_USSD)

		initiate_re = re.compile('[*#]{1,3}1[0-9][0-9].*#')

		if initiate_re.match(ussd):
			return modem.Initiate(ussd)
		elif ussd == "cancel":
			return modem.Cancel()
		else:
			return modem.Respond(ussd)

	def getInfo(self, path):
		infos = {}

		bus = dbus.SystemBus()
		proxy = bus.get_object(MM_DBUS_SERVICE, self._getModemPath(path))

		props = dbus.Interface(proxy, dbus_interface=DBUS_INTERFACE_PROPERTIES)

		mtype = props.Get(MM_DBUS_INTERFACE_MODEM, 'Type')

		if mtype == 1:
			infos['Type'] = 'GSM'
		elif mtype == 2:
			infos['Type'] = 'CDMA'

		infos['Driver'] = str(props.Get(MM_DBUS_INTERFACE_MODEM, 'Driver'))
		infos['Modem device'] = str(props.Get(MM_DBUS_INTERFACE_MODEM, 'MasterDevice'))
		infos['Data device'] = str(props.Get(MM_DBUS_INTERFACE_MODEM, 'Device'))
		infos['Device ID'] = str(props.Get(MM_DBUS_INTERFACE_MODEM, 'DeviceIdentifier'))

		modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM)
		info = modem.GetInfo()

		infos['Vendor'] = str(info[0])
		infos['Model'] = str(info[1])
		infos['Version'] = str(info[2])

		if mtype == 1:
			infos.update(self.gsm_inspect(proxy, props))
		elif mtype == 2:
			infos.update(self.cdma_inspect(proxy, props))

		return infos

	def getGsmNetworkMode(self, path):
		bus = dbus.SystemBus()

		proxy = bus.get_object(MM_DBUS_SERVICE, self._getModemPath(path))
		mode = proxy.GetNetworkMode()

		if mode == 0x0:
			mode = "Unknown"
		elif mode == 0x1:
			mode = "Any"
		elif mode == 0x2:
			mode = "GPRS"
		elif mode == 0x4:
			mode = "EDGE"
		elif mode == 0x8:
			mode = "UMTS"
		elif mode == 0x10:
			mode = "HSDPA"
		elif mode == 0x20:
			mode = "2G Preferred"
		elif mode == 0x40:
			mode = "3G Preferred"
		elif mode == 0x80:
			mode = "2G Only"
		elif mode == 0x100:
			mode = "3G Only"
		elif mode == 0x200:
			mode = "HSUPA"
		elif mode == 0x400:
			mode = "HSPA"
		else:
			mode = "(Unknown)"

		return mode

	def getGsmBand(self, path):
		bus = dbus.SystemBus()

		proxy = bus.get_object(MM_DBUS_SERVICE, self._getModemPath(path))
		band = proxy.GetBand()

		if band == 0x0:
			band = "Unknown"
		elif band == 0x1:
			band = "Any"
		elif band == 0x2:
			band = "EGSM (900 MHz)"
		elif band == 0x4:
			band = "DCS (1800 MHz)"
		elif band == 0x8:
			band = "PCS (1900 MHz)"
		elif band == 0x10:
			band = "G850 (850 MHz)"
		elif band == 0x20:
			band = "U2100 (WCSMA 2100 MHZ, Class I)"
		elif band == 0x40:
			band = "U1700 (WCDMA 3GPP UMTS1800 MHz, Class III)"
		elif band == 0x80:
			band = "17IV (WCDMA 3GPP AWS 1700/2100 MHz, Class IV)"
		elif band == 0x100:
			band = "U800 (WCDMA 3GPP UMTS800 MHz, Class VI)"
		elif band == 0x200:
			band = "U850 (WCDMA 3GPP UMT850 MHz, Class V)"
		elif band == 0x400:
			band = "U900 (WCDMA 3GPP UMTS900 MHz, Class VIII)"
		elif band == 0x800:
			band = "U17IX (WCDMA 3GPP UMTS MHz, Class IX)"
		else:
			band = "(invalid)"

		return band

	def get_cdma_band_class(self, band_class):
		if band_class == 1:
			return "800MHz"
		elif band_class == 2:
			return "1900MHz"
		else:
			return "Unknown"

	def get_reg_state(self, state):
		if state == 1:
			return "registered (roaming unknown)"
		elif state == 2:
			return "registered on home network"
		elif state == 3:
			return "registered on roaming network"
		else:
			return "unknown"

	def cdma_inspect(self, proxy, props):
		infos = {}
		cdma = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_CDMA)

		esn = cdma.GetEsn()
		infos['ESN'] = esn

		try:
			(cdma_1x_state, evdo_state) = cdma.GetRegistrationState()
			infos['1x State'] = self.get_reg_state (cdma_1x_state)
			infos['EVDO State'] = self.get_reg_state (evdo_state)
		except dbus.exceptions.DBusException, e:
			pass

		try:
			quality = cdma.GetSignalQuality()
			infos['Signal quality'] = quality
		except dbus.exceptions.DBusException, e:
			pass

		try:
			info = cdma.GetServingSystem()
			infos['Class'] = self.get_cdma_band_class(info[0])
			infos['Band'] = info[1]
			infos['SID'] = info[2]
		except dbus.exceptions.DBusException, e:
			pass

		return infos

	def gsm_inspect(self, proxy, props):
		infos = {}
		card = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_GSM_CARD)
		simid = "<unavailable>"
		try:
			simid = props.Get(MM_DBUS_INTERFACE_GSM_CARD, "SimIdentifier")
		except dbus.exceptions.DBusException:
			pass
		infos['SIM ID'] = str(simid)

		imei = "<unavailable>"
		try:
			imei = card.GetImei()
		except dbus.exceptions.DBusException:
			pass
		infos['IMEI'] = str(imei)

		imsi = "<unavailable>"
		try:
			imsi = card.GetImsi()
		except dbus.exceptions.DBusException:
			pass
		infos['IMSI'] = str(imsi)

		opid = "<unavailable>"
		try:
			opid = card.GetOperatorId()
		except dbus.exceptions.DBusException:
			pass
		infos['Operator ID'] = opid

		net = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_GSM_NETWORK)
		try:
			quality = net.GetSignalQuality()
			infos['Signal quality'] = quality
		except dbus.exceptions.DBusException, e:
			pass

		try:
			reg = net.GetRegistrationInfo()
			infos['Reg status'] = (mm_reg[int(reg[0])], str(reg[1]), str(reg[2]))
		except dbus.exceptions.DBusException, e:
			pass

		try:
			allowed = props.Get(MM_DBUS_INTERFACE_MODEM_GSM_NETWORK, "AllowedMode")
			infos['Allowed mode'] = mm_allowed[allowed]
		except dbus.exceptions.DBusException, e:
			pass

		try:
			act = props.Get(MM_DBUS_INTERFACE_MODEM_GSM_NETWORK, "AccessTechnology")
			infos['Access Tech'] = mm_act[act]
		except dbus.exceptions.DBusException, e:
			pass

		return infos
